package manet.comm;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.util.ArrayList;


public class MulticastIPCommunicator implements Communicator {

	/** Maximum supported message byte size */
	public static final int MAX_BYTES = 1024;
	
	/** Default port for communication */
	public static final int PORT = 12005;
	
	/** Internal listener for receiving messages */
	private Thread listener = null;
	
	/** Internal queue for storing received messages */
	private ArrayList<String> messageQueue = null;
	
	/** InetSocket Address for Sending */
	private InetSocketAddress address = null;

	/** Multicast Group for Listening */
	private String group = null;
	
	/** Status for receiving messages */
	private boolean listening;
	
	/** Default constructor */
	public MulticastIPCommunicator() {
		this("192.168.239.1", "239.239.239.239", MulticastIPCommunicator.PORT);
	}
	
	/** Constructor allowing ability to specify group and port */
	public MulticastIPCommunicator(String addr, String group, int port) {
		this.messageQueue = new ArrayList<String>();
		try {
			this.address = new InetSocketAddress(InetAddress.getByName(addr), port);
		} catch (UnknownHostException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		this.group = group;
		
		try {
			this.listener = new MulticastIPListener(this.group, this.address, this);
		} catch (UnknownHostException e) {
			System.out.println("Unrecognized group address - defaulting to localhost");
			e.printStackTrace();
		}
		this.listening = false;
	}
	
	/** This method overloads the sendMessage method requiring a destination */
	public void sendMessage(String message) {
		sendMessage(message, this.group);
	}

	/** This method is used for sending a message to the broadcast 
	 * address on the default port 
	 * */
	public void sendMessage(String message, String entityName) {
		MulticastSocket socket = null;
		try {
			socket = new MulticastSocket();
			InetAddress destAddress = InetAddress.getByName(entityName);
			byte[] messageData = message.getBytes();
			DatagramPacket packet = new DatagramPacket(messageData, message.length(), destAddress, this.address.getPort());
			socket.send(packet);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(socket != null) {
				socket.close();
				socket = null;
			}
		}
		
	}

	/** Returns true if currently listening */
	public boolean isListening() {
		return this.listening;
	}
	
	public void setListeningState(boolean newState) {
		// check if already in the requested state
		if(this.listening == newState) return;
		if(newState) {
			// start this thread up
			this.listening = true;
			this.listener.start();
		} else {
			// shut the listener down
			this.listening = false;
			this.listener.interrupt();
		}
	}
	
	/** Returns oldest message on queue */
	public String getQueuedMessage() {
		if (!this.messageQueue.isEmpty()) {
			return messageQueue.remove(0);
		} else return null;
	}

	/** Returns whether new messages are available in the queue */
	public boolean newMessagesAvailable() {
		return !this.messageQueue.isEmpty();
	}

	/** Internal method used by thread for storing queued messages */
	protected void queueMessage(String message) {
		this.messageQueue.add(message);
	}
}
